前端实现苹果官网 的滚动动画 时间轴动画、序列动画实现解析

您所在的位置:网站首页 mac book air鼠标怎么上下滑动 前端实现苹果官网 的滚动动画 时间轴动画、序列动画实现解析

前端实现苹果官网 的滚动动画 时间轴动画、序列动画实现解析

2024-07-17 07:21| 来源: 网络整理| 查看: 265

苹果的官网一直是引领者前端网页效果的发展,本文对苹果mac book的宣传页面前端实现做一个实现步骤的解析和复现  使用框架   react   ts

首先看原网页效果:

苹果macair网页效果

首先观察页面,随着页面滚动,开头一个标题文字逐渐放大,放到最大之后标题消失然后出现笔记本的元素随着滚动逐渐打开,然后出现笔记本文字,注意: 这些元素没有随着滚动而往下动,而是吸顶定位,ok  现在页面大概方式大概理完了,开始下一步

笔记本和标题为什么滚动的时候没有跟着滚动?

直接讲实现方式吧,   position: sticky;   top:0    粘性布局,  最外层的总div设置的非常高,比如800vh  ,就是八个屏幕的高度,内部的元素都堆叠在一起控制显示和隐藏,然后内部的这些元素的小div就是大概100vh的高度   设置粘性布局,top:0,这个时候就发现滚动的时候里边的div就固定在中间了,等大div  800vh滚动结束的时候内部div才会随着滚动,这个时候发现整体的布局就结束了,进行下一步 , 操作元素的显示隐藏等动画时间轴

操作元素的时间轴动画

上一步已经布局好了,假设布局中共计三个子元素,包含一个h1标题,一个笔记本元素,一个笔记本名称,这三个元素需要在不同的滚动节点出现,并可以设置不同的样式,并且随着滚动的距离控制动画的进度,并且支持回滚等操作,一听这就是十分复杂的,其中笔记本还随着滚动有开合的效果,先告诉大家笔记本是怎么实现的

笔记本随滚动的开合效果:  笔记本是个视频,滚动的时候根据滚动距离设置视频的当前帧时间定位,属性是  currentTime  ,操作方式就是   dom.currentTime = *  就行

那现在开始思考如何让这三个元素紧密衔接进行动画操作?假设整个div的800vw是个时间轴 共计100%,那么监控这个div距离顶部的距离就可以得到滚动了多少尺寸,通过比值计算就可以得到滚动的百分比,得到当前滚动到了百分之多少的位置,假设提前定义好了不同的百分比距离的样式,那么根据这个就可以计算该设置的样式了,先看一张图

 红色圈起来的就是我的组件的配置项,我定义了三个元素的不同节点的时候该展示什么样的样式,专业叫法叫关键帧,比如像h1,滚动到0%的时候我定义透明度 0.1,大小 1倍,滚动到15%的时候透明度1,大小一倍,讲到这里可能你稍微有了一点想法,我把元素的变化的关键帧都配置好,通过id(有点low用id)去进行关联元素,这样的话我再拿到当前滚动的百分比,我就可以判断当前滚动到了那个关键帧,假设滚动到了   9%,那么我就循环所有的配置项,一个一个的找到元素9%的时候该是什么样式,比如h1元素,就是在   0% 关键帧到 15%关键帧之间,这里有点复杂需要理解下,就是获取到两个前后关键帧才能计算他们的样式该是什么,下文成为前后帧,9%  在1  到  15 这俩前后帧中处在  60%(这里怎么计算需要自己感悟,9 / 15 / 100 = 60 %),百分之60再计算前后帧之间样式的差,比如 0帧的时候透明度 0.1   15关键帧透明度1,那么前后帧的透明度差就是0.9  再  * 计算出的所处位置  60%  就等于 0.54 透明度,然后根据id修改元素的样式就行了,这里的计算方法是核心复杂点,需要好好理解消化,计算方法已经写成了通用的组件,可以看下文

总结

看的云里雾里的,再总结梳理一下,定义好时间轴配置,每个元素到哪个节点该干什么,然后求div滚动的百分比得到滚动到哪了,然后根据滚动到的位置百分比得值去求时间轴配置里边找关键帧,然后找到前后关键帧之后计算该设置什么样式,设置样式就行了,看不懂的话就直接拿下边代码直接用吧,react 函数式写法,ts,支持常见样式变化

最终通用组件代码 import React, { cloneElement, useEffect, useRef, useState } from "react" import './index.less' import { isBrowser } from "@utils/util" export default ({children, config, ...rest}:any)=> { const [refList, setRefList] = useState({}) const { props } = children || {} const { children:childrenList, ref, ...rest1 } = props || {} const allRef = ref ? ref : useRef(null) as any const timeRef = useRef(null) // 6,在这里计算前后帧和滚动百分比,得到该设置的值 const handleStyle = (dom: HTMLElement | null, endStyle: any, beginStyle: any, value: number)=> { const endStyleList = Object.keys(endStyle) as any endStyleList.forEach((key:any , index: number)=> { const itemStyle = endStyle[key] if(key === 'scale'){ dom.style.transform = `scale(${(endStyle[key] - beginStyle[key])*value / 100 + beginStyle[key]})` } else if(key === 'top'){ dom.style.transform = `translateY(${(endStyle[key] - beginStyle[key])*value / 100}px)` } else if(key === 'currentTime'){ requestAnimationFrame(()=> dom[key] = `${(endStyle[key] - beginStyle[key])*value / 100 || value}`) } else { dom.style[key] = `${(endStyle[key] - beginStyle[key]) * value / 100 + beginStyle[key]}` } }); } const handleScroll = () => { // 2, 计算出接收的组件的位置高度距离信息 // 动画容器距离网页顶部的滚动距离 const { top } = allRef.current.parentNode.getBoundingClientRect() || {top: 0} // 网页可视高度 const viewHeight = document.body.clientHeight // 动画容器的高度 + 0.5页面高度为了优化结束的时候的效果 const all_height = isBrowser() ? allRef.current?.parentNode.scrollHeight + 0.5 * viewHeight: 1200 //页面显示区域总高度 // 计算动画容器距离底部网上滚动的多少 const top_value = -top + viewHeight // 根据动画元素容器滚动的距离计算出已经滚动距离的百分比 let proportion = 100 - (all_height - top_value)/all_height * 100 const proportionValue = proportion = 100 ? 100 : proportion) // 3,遍历接收的子节点,有配置时间轴的话就去做动画处理 childrenList.forEach((element:any) => { const { id } = element.props let configList = config[id] let dom = document.getElementById(id) if(configList){ const config = Object.keys(configList) as any for (let index = 0; index < config.length; index++) { const key = config[index]; const lastKey = config[(index === 0 || (index === config.length-1 && proportionValue >= config[index])) ? index : index - 1 ] if( Number(key) >= proportionValue || index === config.length -1){ // // 4, 计算当前阶段走到的比例值 const now_value = key === lastKey ? 100 : (1 - (key - proportionValue) / (key - lastKey)) * 100 // requestAnimationFrame(()=> handleStyle(dom, configList[key], configList[lastKey], now_value )) // 5, 定义的设置dom样式的方法,传进去dom 前后帧,当前的滚动位置百分比 handleStyle(dom, configList[key], configList[lastKey], now_value ) break } } } else { return } }); } useEffect(()=> { // 1 , 设置滚动监听事件 约17毫秒一次, handleScroll() isBrowser() && window.addEventListener('scroll', handleScroll); return (()=> { isBrowser() && window.removeEventListener('scroll', handleScroll) }) }, []) return { childrenList.map((element: any, index: number) => { const { className, style, ...rest } = element.props return cloneElement(element,{...element.props, className: className, } ) }) } } 使用方法 { title: { 0: {opacity: 0.1,scale:1 }, 15: {opacity: 1,scale:1}, 40: {opacity: 1,scale:260}, 45: {opacity: 1,scale:340}, 50:{opacity: 1,scale:580.5}, 51:{opacity: 0,scale:595}, }, video: { 0: {opacity: 0, currentTime:0}, 50:{opacity: 0,currentTime:2.5}, 51:{opacity: 1,currentTime:2.52}, 100:{opacity: 1, currentTime:5.5}, }, span1: { 49:{scale:0, opacity: 0,}, 65: {scale:1 , opacity: 0,}, 72: {scale:1, opacity: 1,}, 80 : {scale:1, opacity: 0,}, }, }} 华为云服务 啊

注意最外层div设置的要高一点,内部div定位方式要设置一下,拿过去就可以直接  react使用,效果基本还原一致,在动画框架中还没看到相似功能的,代码比较烂,仅供参考



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3